using System;
using System.ComponentModel;
using System.CodeDom;
using System.Collections;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.Parser;
using ICSharpCode.NRefactory.Visitors;
using ICSharpCode.NRefactory.PrettyPrinter;

namespace CodeDomAssistant
{
	/// <summary>
    /// Summary description for CodeDomCodeProvider.
	/// </summary>
	public class CodeDomCodeProvider : System.CodeDom.Compiler.CodeDomProvider
	{
        
		public CodeDomCodeProvider()
            : base()
		{
		}

        public override string FileExtension
        {
            get
            {
                return "cs";
            }
        }

        public override System.CodeDom.Compiler.ICodeGenerator CreateGenerator()
        {
            return new CodeGenerator();
        }

        public override System.CodeDom.Compiler.ICodeCompiler CreateCompiler()
        {
            return null;
        }
	}

    /// <summary>
    /// Generate CodeDom into a C# CodeDom Source File
    /// </summary>
    public class CodeGenerator : System.CodeDom.Compiler.ICodeGenerator
    {
        Hashtable codedomVar = new Hashtable(); 

        public CodeGenerator()
            : base()
        {
        }

        /// <summary>
        /// Construct valid C# Name
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        string MakeName(string name)
        {
            name = name.Replace(".", "_");

            return "_" + name;
        }

        /// <summary>
        /// Get Next Variable Name to ensure no collisions
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        string GetNextVar(string name)
        {
            if (name == string.Empty)
            {
                name = "codedom";
            }
            if (name.Contains("`"))
            {
                name = name.Replace("`", "_GenericType_");
            }
            if (codedomVar.ContainsKey(name))
            {
                int id = ((int)codedomVar[name]) + 1;
                codedomVar[name] = id;

                return MakeName(name) + id.ToString();
            }

            codedomVar[name] = 1;

            return MakeName(name) + "1";
        }

        #region ICodeGenerator Members

        public bool Supports(System.CodeDom.Compiler.GeneratorSupport supports)
        {
            // TODO:  Add MyCodeGenerator.Supports implementation
            return true;
        }

        public void GenerateCodeFromType(CodeTypeDeclaration e, System.IO.TextWriter w, System.CodeDom.Compiler.CodeGeneratorOptions o)
        {
            GenerateCodeTypeDeclaration(null, e, w, o);
        }

        public string CreateValidIdentifier(string value)
        {
            return GetNextVar(value);
        }

        public void GenerateCodeFromNamespace(CodeNamespace e, System.IO.TextWriter w, System.CodeDom.Compiler.CodeGeneratorOptions o)
        {
            GenerateCodeNamespace(null, e, w, o);
        }

        public void GenerateCodeFromCompileUnit(CodeCompileUnit e, System.IO.TextWriter w, System.CodeDom.Compiler.CodeGeneratorOptions o)
        {
            GenerateCodeCompileUnit(e, w, o);
        }

        public void GenerateCodeFromExpression(CodeExpression e, System.IO.TextWriter w, System.CodeDom.Compiler.CodeGeneratorOptions o)
        {
            GenerateCodeExpression(null, e, w, o);
        }

        public void GenerateCodeFromStatement(CodeStatement e, System.IO.TextWriter w, System.CodeDom.Compiler.CodeGeneratorOptions o)
        {
            GenerateCodeStatement(null, e, w, o);
        }

        public void ValidateIdentifier(string value)
        {
            // TODO:  Add MyCodeGenerator.ValidateIdentifier implementation
        }

        public string GetTypeOutput(CodeTypeReference type)
        {
//            this.GenerateCodeTypeReference(null, type, w, o);
            // TODO:  Add MyCodeGenerator.GetTypeOutput implementation
            return null;
        }

        public string CreateEscapedIdentifier(string value)
        {
            // TODO:  Add MyCodeGenerator.CreateEscapedIdentifier implementation
            return null;
        }

        public bool IsValidIdentifier(string value)
        {
            // TODO:  Add MyCodeGenerator.IsValidIdentifier implementation
            return false;
        }

        #endregion

        string GenerateCodeAttributeArgument(string context, CodeAttributeArgument argument, System.IO.TextWriter w, System.CodeDom.Compiler.CodeGeneratorOptions o)
        {
            string name = GetNextVar(argument.Name + "_arg");

            string exprName = GenerateCodeExpression(null, argument.Value, w, o);

            Debug.Assert(exprName != null);

            w.WriteLine(@"CodeAttributeArgument {0} = new CodeAttributeArgument(""{1}"", {2});", name, argument.Name, exprName);
            if (context != null && context.Length > 0)
            {
                w.WriteLine(@"{0}.Add({1});", context, name);
                w.WriteLine();
            }

            return name;
        }

        void GenerateCodeAttributeArguments(string context, CodeAttributeArgumentCollection arguments, System.IO.TextWriter w, System.CodeDom.Compiler.CodeGeneratorOptions o)
        {
            // arguments
            if (arguments.Count > 0)
            {
                foreach (CodeAttributeArgument arg in arguments)
                {
                    GenerateCodeAttributeArgument(context, arg, w, o);
                }
            }
        }

        string GenerateCodeAttributeDeclaration(string context, CodeAttributeDeclaration attribute, System.IO.TextWriter w, System.CodeDom.Compiler.CodeGeneratorOptions o)
        {
            string name = GetNextVar(attribute.Name + "_attr");

            w.WriteLine(@"CodeAttributeDeclaration {0} = new CodeAttributeDeclaration(""{1}"");", name, attribute.Name);
            this.GenerateCodeAttributeArguments(string.Format(@"{0}.Arguments"), attribute.Arguments, w, o);

            if (context != null && context.Length > 0)
            {
                w.WriteLine(@"{0}.Add({1});", context, name);
                w.WriteLine();
            }

            return name;
        }

        void GenerateCodeAttributeDeclarations(string context, CodeAttributeDeclarationCollection attributes, System.IO.TextWriter w, System.CodeDom.Compiler.CodeGeneratorOptions o)
        {
            // attributes
            if (attributes.Count > 0)
            {
                foreach (CodeAttributeDeclaration attr in attributes)
                {
                    GenerateCodeAttributeDeclaration(context, attr, w, o);
                }
            }
        }

        string GenerateCodeCatchClause(string context, CodeCatchClause catchclause, System.IO.TextWriter w, System.CodeDom.Compiler.CodeGeneratorOptions o)
        {
            string name = GetNextVar(catchclause.LocalName + "_exception");

            w.WriteLine(@"CodeCatchClause {0} = new CodeCatchClause();", name);

            if (catchclause.LocalName != null && catchclause.LocalName.Length > 0)
            {
                w.WriteLine(@"{0}.LocalName = ""{1}"";", name, catchclause.LocalName);
            }

            w.WriteLine(@"{0}.CatchExceptionType = {1};", name, this.GenerateCodeTypeReference(null, catchclause.CatchExceptionType, w, o));

            GenerateCodeStatements(string.Format(@"{0}.Statements", name), catchclause.Statements, w, o);

            if (context != null && context.Length > 0)
            {
                w.WriteLine(@"{0}.Add({1});", context, name);
                w.WriteLine();
            }

            return name;
        }

        void GenerateCodeCatchClauses(string context, CodeCatchClauseCollection catchclauses, System.IO.TextWriter w, System.CodeDom.Compiler.CodeGeneratorOptions o)
        {
            // catchclauses
            if (catchclauses.Count > 0)
            {
                foreach (CodeCatchClause catchclause in catchclauses)
                {
                    GenerateCodeCatchClause(context, catchclause, w, o);
                }
            }
        }

        string GenerateCodeComment(string context, CodeComment comment, System.IO.TextWriter w, System.CodeDom.Compiler.CodeGeneratorOptions o)
        {
            string name = GetNextVar("comment");

            w.WriteLine(@"CodeComment {0} = new CodeComment();", name);

            if (comment.DocComment)
            {
                w.WriteLine(@"{0}.DocComment = {1};", name, comment.DocComment.ToString().ToLower());
            }

            if (comment.Text != null && comment.Text.Length > 0)
            {
                w.WriteLine(@"{0}.Text = ""{1}"";", name, comment.Text);
            }

            if (context != null && context.Length > 0)
            {
                w.WriteLine(@"{0}.Add({1});", context, name);
                w.WriteLine();
            }

            return name;
        }

        void GenerateCodeComments(string context, CodeCommentStatementCollection comments, System.IO.TextWriter w, System.CodeDom.Compiler.CodeGeneratorOptions o)
        {
            // comments
            if (comments.Count > 0)
            {
                foreach (CodeCommentStatement comment in comments)
                {
                    w.WriteLine(@"{0}.Add(new CodeCommentStatement(""{1}"", {2}));", context, comment.Comment.Text, comment.Comment.DocComment);
                }
            }
        }

        void GenerateCodeCompileUnit(CodeCompileUnit e, System.IO.TextWriter w, System.CodeDom.Compiler.CodeGeneratorOptions o)
        {
            string name = this.GetNextVar("compileunit");

            w.WriteLine(@"// CodeDom Compile Unit");
            w.WriteLine(@"CodeCompileUnit {0} = new CodeCompileUnit();", name);

            if (e.ReferencedAssemblies.Count > 0)
            {
                if (o.BlankLinesBetweenMembers)
                {
                    w.WriteLine();
                }

                w.WriteLine(@"// Referenced Assemblies");
                foreach (string refassembly in e.ReferencedAssemblies)
                {
                    w.WriteLine(@"{0}.ReferencedAssemblies.Add(""{1}"");", name, refassembly);
                }
            }

            GenerateCodeNamespaces(string.Format("{0}.Namespaces", name), e.Namespaces, w, o);
        }

        string GenerateCodeEventReferenceExpression(string context, CodeEventReferenceExpression e, System.IO.TextWriter w, System.CodeDom.Compiler.CodeGeneratorOptions o)
        {
            CodeEventReferenceExpression expr = (CodeEventReferenceExpression)e;

            string name = GetNextVar("event");

            w.WriteLine(@"CodeEventReferenceExpression {0} = new CodeEventReferenceExpression();", name);

            w.WriteLine(@"{0}.EventName = ""{1}"";", name, expr.EventName);
            w.WriteLine(@"{0}.TargetObject = {1};", name, GenerateCodeExpression(null, expr.TargetObject, w, o));

            if (context != null && context.Length > 0)
            {
                w.WriteLine(@"{0}.Add({1});", context, name);
                w.WriteLine();
            }

            return name;
        }

        string GenerateCodeExpression(string context, CodeExpression e, System.IO.TextWriter w, System.CodeDom.Compiler.CodeGeneratorOptions o)
        {
            if (e == null)
            {
                return "null";
            }

            if (e is CodeArrayCreateExpression)
            {
                CodeArrayCreateExpression expr = (CodeArrayCreateExpression)e;

                string name = GetNextVar("arr");

                w.WriteLine(@"CodeArrayCreateExpression {0} = new CodeArrayCreateExpression();", name);

                if (expr.CreateType != null)
                {
                    string type = GenerateCodeTypeReference(null, expr.CreateType, w, o);
                    w.WriteLine(@"{0}.CreateType = {1};", name, type);
                }

                GenerateCodeExpressions(string.Format(@"{0}.Initializers", name), expr.Initializers, w, o);

                w.WriteLine(@"{0}.Size = {1};", name, expr.Size);

                if (expr.SizeExpression != null)
                {
                    string sizeexpr = GenerateCodeExpression(null, expr.SizeExpression, w, o);
                    w.WriteLine(@"{0}.SizeExpression = {1};", name, sizeexpr);
                }

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            if (e is CodeBaseReferenceExpression) 
            {
                CodeBaseReferenceExpression expr = (CodeBaseReferenceExpression)e;

                string name = GetNextVar("base");

                w.WriteLine(@"CodeBaseReferenceExpression {0} = new CodeBaseReferenceExpression();", name);
                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            if (e is CodeBinaryOperatorExpression) 
            {
                CodeBinaryOperatorExpression expr = (CodeBinaryOperatorExpression)e;

                string name = GetNextVar("binop");

                w.WriteLine(@"CodeBinaryOperatorExpression {0} = new CodeBinaryOperatorExpression();", name);

                w.WriteLine(@"{0}.Left = {1};", name, GenerateCodeExpression(null, expr.Left, w, o));
                w.WriteLine(@"{0}.Operator = {1};", name, GetCodeBinaryOperatorType(expr.Operator));
                w.WriteLine(@"{0}.Right = {1};", name, GenerateCodeExpression(null, expr.Right, w, o));

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            if (e is CodeCastExpression) 
            {
                CodeCastExpression expr = (CodeCastExpression)e;

                string name = GetNextVar("cast");

                w.WriteLine(@"CodeCastExpression {0} = new CodeCastExpression();", name);

                w.WriteLine(@"{0}.Expression = {1};", name, GenerateCodeExpression(null, expr.Expression, w, o));
                w.WriteLine(@"{0}.TargetType = {1};", name, this.GenerateCodeTypeReference(null, expr.TargetType, w, o));

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            if (e is CodeDelegateCreateExpression) 
            {
                CodeDelegateCreateExpression expr = (CodeDelegateCreateExpression)e;

                string name = GetNextVar("delegate");

                w.WriteLine(@"CodeDelegateCreateExpression {0} = new CodeDelegateCreateExpression();", name);

                w.WriteLine(@"{0}.DelegateType = {1};", name, this.GenerateCodeTypeReference(null, expr.DelegateType, w, o));
                w.WriteLine(@"{0}.MethodName = ""{1}"";", name, expr.MethodName);
                w.WriteLine(@"{0}.TargetObject = {1};", name, GenerateCodeExpression(null, expr.TargetObject, w, o));

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            if (e is CodeFieldReferenceExpression) 
            {
                CodeFieldReferenceExpression expr = (CodeFieldReferenceExpression)e;

                string name = GetNextVar("field");

                w.WriteLine(@"CodeFieldReferenceExpression {0} = new CodeFieldReferenceExpression();", name);

                w.WriteLine(@"{0}.FieldName = ""{1}"";", name, expr.FieldName);
                w.WriteLine(@"{0}.TargetObject = {1};", name, GenerateCodeExpression(null, expr.TargetObject, w, o));

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            if (e is CodeArgumentReferenceExpression) 
            {
                CodeArgumentReferenceExpression expr = (CodeArgumentReferenceExpression)e;

                string name = GetNextVar("arg");

                w.WriteLine(@"CodeArgumentReferenceExpression {0} = new CodeArgumentReferenceExpression();", name);

                w.WriteLine(@"{0}.ParameterName = ""{1}"";", name, expr.ParameterName);

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            if (e is CodeVariableReferenceExpression) 
            {
                CodeVariableReferenceExpression expr = (CodeVariableReferenceExpression)e;

                string name = GetNextVar("arg");

                w.WriteLine(@"CodeVariableReferenceExpression {0} = new CodeVariableReferenceExpression();", name);

                w.WriteLine(@"{0}.VariableName = ""{1}"";", name, expr.VariableName);

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            if (e is CodeIndexerExpression) 
            {
                CodeIndexerExpression expr = (CodeIndexerExpression)e;

                string name = GetNextVar("index");

                w.WriteLine(@"CodeIndexerExpression {0} = new CodeIndexerExpression();", name);

                GenerateCodeExpressions(string.Format(@"{0}.Indices", name), expr.Indices, w, o);

                w.WriteLine(@"{0}.TargetObject = {1};", name, GenerateCodeExpression(null, expr.TargetObject, w, o));

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            if (e is CodeArrayIndexerExpression) 
            {
                CodeArrayIndexerExpression expr = (CodeArrayIndexerExpression)e;

                string name = GetNextVar("arrindex");

                w.WriteLine(@"CodeArrayIndexerExpression {0} = new CodeArrayIndexerExpression();", name);

                GenerateCodeExpressions(string.Format(@"{0}.Indices", name), expr.Indices, w, o);

                w.WriteLine(@"{0}.TargetObject = {1};", name, GenerateCodeExpression(null, expr.TargetObject, w, o));

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            if (e is CodeSnippetExpression) 
            {
                CodeSnippetExpression expr = (CodeSnippetExpression)e;

                string name = GetNextVar("snippet");

                w.WriteLine(@"CodeSnippetExpression {0} = new CodeSnippetExpression();", name);

                w.WriteLine(@"{0}.Value = ""{1}"";", name, Escape(expr.Value));

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            if (e is CodeMethodInvokeExpression) 
            {
                CodeMethodInvokeExpression expr = (CodeMethodInvokeExpression)e;

                string name = GetNextVar("invoke");

                w.WriteLine(@"CodeMethodInvokeExpression {0} = new CodeMethodInvokeExpression();", name);

                GenerateCodeExpressions(string.Format(@"{0}.Parameters", name), expr.Parameters, w, o);
                
                w.WriteLine(@"{0}.Method = {1};", name, GenerateCodeMethodReferenceExpression(null, expr.Method, w, o));

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            if (e is CodeMethodReferenceExpression) 
            {
                CodeMethodReferenceExpression expr = (CodeMethodReferenceExpression)e;

                string name = GetNextVar("method");

                w.WriteLine(@"CodeMethodReferenceExpression {0} = new CodeMethodReferenceExpression();", name);

                w.WriteLine(@"{0}.MethodName = ""{1}"";", name, expr.MethodName);
                w.WriteLine(@"{0}.TargetObject = {1};", name, GenerateCodeExpression(null, expr.TargetObject, w, o));

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            if (e is CodeEventReferenceExpression) 
            {
                return GenerateCodeEventReferenceExpression(context, (CodeEventReferenceExpression)e, w, o);
            }
            
            if (e is CodeDelegateInvokeExpression) 
            {
                CodeDelegateInvokeExpression expr = (CodeDelegateInvokeExpression)e;

                string name = GetNextVar("invokedelegate");

                w.WriteLine(@"CodeDelegateInvokeExpression {0} = new CodeDelegateInvokeExpression();", name);

                GenerateCodeExpressions(string.Format(@"{0}.Parameters", name), expr.Parameters, w, o);

                w.WriteLine(@"{0}.TargetObject = {1};", name, GenerateCodeExpression(null, expr.TargetObject, w, o));

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            if (e is CodeObjectCreateExpression) 
            {
                CodeObjectCreateExpression expr = (CodeObjectCreateExpression)e;

                string name = GetNextVar("new");

                w.WriteLine(@"CodeObjectCreateExpression {0} = new CodeObjectCreateExpression();", name);

                w.WriteLine(@"{0}.CreateType = {1};", name, GenerateCodeTypeReference(null, expr.CreateType, w, o));
                
                GenerateCodeExpressions(string.Format(@"{0}.Parameters", name), expr.Parameters, w, o);

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            if (e is CodeParameterDeclarationExpression) 
            {
                return GenerateCodeParameterDeclarationExpression(context, (CodeParameterDeclarationExpression)e, w, o);
            }

            if (e is CodeDirectionExpression) 
            {
                CodeDirectionExpression expr = (CodeDirectionExpression)e;

                string name = GetNextVar("dir");

                w.WriteLine(@"CodeDirectionExpression {0} = new CodeDirectionExpression();", name);

                w.WriteLine(@"{0}.Direction = {1};", name, this.GetFieldDirection(expr.Direction));
                w.WriteLine(@"{0}.Expression = {1};", name, GenerateCodeExpression(null, expr.Expression, w, o));

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            if (e is CodePrimitiveExpression) 
            {
                CodePrimitiveExpression expr = (CodePrimitiveExpression)e;

                string name = GetNextVar("value");

                w.WriteLine(@"CodePrimitiveExpression {0} = new CodePrimitiveExpression();", name);

                // TODO - better primitive support
                if (expr.Value == null)
                {
                    w.WriteLine(@"{0}.Value = null;", name);
                }
                else if (expr.Value.GetType() == typeof(System.Char))
                {
                    w.WriteLine(@"{0}.Value = '{1}';", name, expr.Value.ToString());
                }
                else if (expr.Value.GetType() == typeof(System.String))
                {
                    w.WriteLine(@"{0}.Value = ""{1}"";", name, expr.Value.ToString());
                }
                else if (expr.Value.GetType() == typeof(System.Boolean))
                {
                    w.WriteLine(@"{0}.Value = {1};", name, expr.Value.ToString().ToLower());
                }
                else
                {
                    w.WriteLine(@"{0}.Value = {1};", name, expr.Value.ToString());
                }

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            if (e is CodePropertyReferenceExpression) 
            {
                CodePropertyReferenceExpression expr = (CodePropertyReferenceExpression)e;

                string name = GetNextVar("prop");

                w.WriteLine(@"CodePropertyReferenceExpression {0} = new CodePropertyReferenceExpression();", name);

                w.WriteLine(@"{0}.PropertyName = ""{1}"";", name, expr.PropertyName);
                w.WriteLine(@"{0}.TargetObject = {1};", name, GenerateCodeExpression(null, expr.TargetObject, w, o));

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            if (e is CodePropertySetValueReferenceExpression) 
            {
                CodePropertySetValueReferenceExpression expr = (CodePropertySetValueReferenceExpression)e;

                string name = GetNextVar("setprop");

                w.WriteLine(@"CodePropertySetValueReferenceExpression {0} = new CodePropertySetValueReferenceExpression();", name);
                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            if (e is CodeThisReferenceExpression) 
            {
                CodeThisReferenceExpression expr = (CodeThisReferenceExpression)e;

                string name = GetNextVar("this");

                w.WriteLine(@"CodeThisReferenceExpression {0} = new CodeThisReferenceExpression();", name);
                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }
            
            if (e is CodeTypeReferenceExpression) 
            {
                CodeTypeReferenceExpression expr = (CodeTypeReferenceExpression)e;

                string name = GetNextVar("ref");

                w.WriteLine(@"CodeTypeReferenceExpression {0} = new CodeTypeReferenceExpression();", name);

                w.WriteLine(@"{0}.Type = {1};", name, GenerateCodeTypeReference(null, expr.Type, w, o));

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            if (e is CodeTypeOfExpression) 
            {
                CodeTypeOfExpression expr = (CodeTypeOfExpression)e;

                string name = GetNextVar("typeof");

                w.WriteLine(@"CodeTypeOfExpression {0} = new CodeTypeOfExpression();", name);

                w.WriteLine(@"{0}.Type = {1};", name, GenerateCodeTypeReference(null, expr.Type, w, o));

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            Debug.Fail(string.Format("Generator for CodeExpression Type {0} Not Implemented", e.GetType().Name));

            return null;
        }

        void GenerateCodeExpressions(string context, CodeExpressionCollection exprs, System.IO.TextWriter w, System.CodeDom.Compiler.CodeGeneratorOptions o)
        {
            if (exprs.Count > 0)
            {
                foreach (CodeExpression expr in exprs)
                {
                    GenerateCodeExpression(context, expr, w, o);
                }
            }
        }

        string GenerateCodeDirective(string context, CodeDirective e, System.IO.TextWriter w, System.CodeDom.Compiler.CodeGeneratorOptions o)
        {
            if (e == null)
            {
                return "null";
            }

            if (e is CodeChecksumPragma)
            {
                CodeChecksumPragma directive = (CodeChecksumPragma)e;

                string name = GetNextVar("checksum");

                w.WriteLine(@"CodeChecksumPragma {0} = new CodeChecksumPragma();", name);

                w.WriteLine(@"{0}.ChecksumAlgorithmId = new Guid(""{1}"");", name, directive.ChecksumAlgorithmId.ToString("B"));
                w.WriteLine(@"{0}.ChecksumData = new byte[] { {1} };", name, GetByteData(directive.ChecksumData));
                w.WriteLine(@"{0}.FileName = ""{1}"";", name, directive.FileName);

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            if (e is CodeRegionDirective)
            {
                CodeRegionDirective directive = (CodeRegionDirective)e;

                string name = GetNextVar("region");

                w.WriteLine(@"CodeRegionDirective {0} = new CodeRegionDirective();", name);

                w.WriteLine(@"{0}.RegionMode = {1};", name, GetCodeRegionMode(directive.RegionMode));
                w.WriteLine(@"{0}.RegionText = ""{1}"";", name, directive.RegionText);

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            Debug.Fail(string.Format("Generator for CodeDirective Type {0} Not Implemented", e.GetType().Name));

            return null;
        }

        void GenerateCodeDirectives(string context, CodeDirectiveCollection directives, System.IO.TextWriter w, System.CodeDom.Compiler.CodeGeneratorOptions o)
        {
            if (directives.Count > 0)
            {
                foreach (CodeDirective directive in directives)
                {
                    GenerateCodeDirective(context, directive, w, o);
                }
            }
        }

        string GenerateCodeMethodReferenceExpression(string context, CodeMethodReferenceExpression methodref, System.IO.TextWriter w, System.CodeDom.Compiler.CodeGeneratorOptions o)
        {
            string name = GetNextVar(methodref.MethodName  + "_method");

            w.WriteLine(@"CodeMethodReferenceExpression {0} = new CodeMethodReferenceExpression();", name);

            w.WriteLine(@"{0}.MethodName = ""{1}"";", name, methodref.MethodName);

            w.WriteLine(@"{0}.TargetObject = {1};", name, GenerateCodeExpression(null, methodref.TargetObject, w, o));

            if (context != null && context.Length > 0)
            {
                w.WriteLine(@"{0}.Add({1});", context, name);
                w.WriteLine();
            }

            return name;
        }

        void GenerateCodeNamespaceImports(string context, CodeNamespaceImportCollection imports, System.IO.TextWriter w, System.CodeDom.Compiler.CodeGeneratorOptions o)
        {
            // imports
            if (imports.Count > 0)
            {
                if (o.BlankLinesBetweenMembers)
                {
                    w.WriteLine();
                }

                w.WriteLine(@"// Imports");
                foreach (CodeNamespaceImport import in imports)
                {
                    w.WriteLine(@"{0}.Add(new CodeNamespaceImport(""{1}""));", context, import.Namespace);
                }
            }
        }

        string GenerateCodeNamespace(string context, CodeNamespace ns, System.IO.TextWriter w, System.CodeDom.Compiler.CodeGeneratorOptions o)
        {
            if (o.BlankLinesBetweenMembers)
            {
                w.WriteLine();
            }

            w.WriteLine(@"//");
            w.WriteLine(@"// Namespace {0}", ns.Name);
            w.WriteLine(@"//");

            string name = GetNextVar(ns.Name  + "_namespace");

            w.WriteLine(@"CodeNamespace {0} = new CodeNamespace(""{1}"");", name, ns.Name);

            // comments
            GenerateCodeComments(string.Format("{0}.Comments", name), ns.Comments, w, o);

            // imports
            GenerateCodeNamespaceImports(string.Format("{0}.Imports", name), ns.Imports, w, o);

            // Types
            GenerateCodeTypeDeclarations(string.Format("{0}.Types", name), ns.Types, w, o);

            if (context != null && context.Length > 0)
            {
                w.WriteLine(@"{0}.Add({1});", context, name);
                w.WriteLine();
            }

            return name;
        }

        void GenerateCodeNamespaces(string context, CodeNamespaceCollection namespaces, System.IO.TextWriter w, System.CodeDom.Compiler.CodeGeneratorOptions o)
        {
            if (namespaces.Count > 0)
            {
                if (o.BlankLinesBetweenMembers)
                {
                    w.WriteLine();
                }

                foreach (CodeNamespace ns in namespaces)
                {
                    GenerateCodeNamespace(context, ns, w, o);
                }
            }
        }

        string GenerateCodeParameterDeclarationExpression(string context, CodeParameterDeclarationExpression arg, System.IO.TextWriter w, System.CodeDom.Compiler.CodeGeneratorOptions o)
        {
            string name = GetNextVar(arg.Name + "_arg");

            w.WriteLine(@"CodeParameterDeclarationExpression {0} = new CodeParameterDeclarationExpression({1}, ""{2}"");", name, this.GenerateCodeTypeReference(null, arg.Type, w, o), arg.Name);

            this.GenerateCodeAttributeDeclarations(string.Format(@"{0}.CustomAttributes", name), arg.CustomAttributes, w, o);

            w.WriteLine(@"{0}.Direction = {1};", name, this.GetFieldDirection(arg.Direction));
            w.WriteLine(@"{0}.Name = ""{1}"";", name, arg.Name);
            w.WriteLine(@"{0}.Type = {1};", name, GenerateCodeTypeReference(null, arg.Type, w, o));

            if (context != null && context.Length > 0)
            {
                w.WriteLine(@"{0}.Add({1});", context, name);
                w.WriteLine();
            }
 
            return name;
        }

        void GenerateCodeParameterDeclarationExpressions(string context, CodeParameterDeclarationExpressionCollection args, System.IO.TextWriter w, System.CodeDom.Compiler.CodeGeneratorOptions o)
        {
            if (args.Count > 0)
            {
                foreach (CodeParameterDeclarationExpression arg in args)
                {
                    GenerateCodeParameterDeclarationExpression(context, arg, w, o);
                }
            }
        }

        string GenerateCodeStatement(string context, CodeStatement e, System.IO.TextWriter w, System.CodeDom.Compiler.CodeGeneratorOptions o)
        {
            if (e == null)
            {
                return "null";
            }

            if (e is System.CodeDom.CodeMethodReturnStatement)
            {
                System.CodeDom.CodeMethodReturnStatement stmt = (System.CodeDom.CodeMethodReturnStatement)e;

                string name = GetNextVar("return");

                w.WriteLine(@"CodeMethodReturnStatement {0} = new CodeMethodReturnStatement();", name);

                if (stmt.Expression != null)
                {
                    w.WriteLine(@"{0}.Expression = {1};", name, GenerateCodeExpression(null, stmt.Expression, w, o));
                }

                GenerateCodeDirectives(string.Format(@"{0}.StartDirectives", name), stmt.StartDirectives, w, o);
                GenerateCodeDirectives(string.Format(@"{0}.EndDirectives", name), stmt.EndDirectives, w, o);

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            if (e is CodeCommentStatement) 
            {
                System.CodeDom.CodeCommentStatement stmt = (System.CodeDom.CodeCommentStatement)e;

                string name = GetNextVar("comment");

                w.WriteLine(@"CodeCommentStatement {0} = new CodeCommentStatement();", name);

                w.WriteLine(@"{0}.Comment = {1};", name, GenerateCodeComment(null, stmt.Comment, w, o));

                GenerateCodeDirectives(string.Format(@"{0}.StartDirectives", name), stmt.StartDirectives, w, o);
                GenerateCodeDirectives(string.Format(@"{0}.EndDirectives", name), stmt.EndDirectives, w, o);

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            if (e is CodeMethodReturnStatement) 
            {
                System.CodeDom.CodeMethodReturnStatement stmt = (System.CodeDom.CodeMethodReturnStatement)e;

                string name = GetNextVar("return");

                w.WriteLine(@"CodeMethodReturnStatement {0} = new CodeMethodReturnStatement();", name);

                if (stmt.Expression != null)
                {
                    w.WriteLine(@"{0}.Expression = {1};", name, GenerateCodeExpression(null, stmt.Expression, w, o));
                }

                GenerateCodeDirectives(string.Format(@"{0}.StartDirectives", name), stmt.StartDirectives, w, o);
                GenerateCodeDirectives(string.Format(@"{0}.EndDirectives", name), stmt.EndDirectives, w, o);

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            if (e is CodeConditionStatement) 
            {
                System.CodeDom.CodeConditionStatement stmt = (System.CodeDom.CodeConditionStatement)e;

                string name = GetNextVar("if");

                w.WriteLine(@"CodeConditionStatement {0} = new CodeConditionStatement();", name);

                w.WriteLine(@"{0}.Condition = {1};", name, GenerateCodeExpression(null, stmt.Condition, w, o));

                GenerateCodeStatements(string.Format(@"{0}.TrueStatements", name), stmt.TrueStatements, w, o);
                GenerateCodeStatements(string.Format(@"{0}.FalseStatements", name), stmt.FalseStatements, w, o);

                GenerateCodeDirectives(string.Format(@"{0}.StartDirectives", name), stmt.StartDirectives, w, o);
                GenerateCodeDirectives(string.Format(@"{0}.EndDirectives", name), stmt.EndDirectives, w, o);

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }
            
            if (e is CodeTryCatchFinallyStatement) 
            {
                System.CodeDom.CodeTryCatchFinallyStatement stmt = (System.CodeDom.CodeTryCatchFinallyStatement)e;

                string name = GetNextVar("try");

                w.WriteLine(@"CodeTryCatchFinallyStatement {0} = new CodeTryCatchFinallyStatement();", name);

                GenerateCodeStatements(string.Format(@"{0}.TryStatements", name), stmt.TryStatements, w, o);
                GenerateCodeCatchClauses(string.Format(@"{0}.CatchClauses", name), stmt.CatchClauses, w, o);
                GenerateCodeStatements(string.Format(@"{0}.FinallyStatements", name), stmt.FinallyStatements, w, o);

                GenerateCodeDirectives(string.Format(@"{0}.StartDirectives", name), stmt.StartDirectives, w, o);
                GenerateCodeDirectives(string.Format(@"{0}.EndDirectives", name), stmt.EndDirectives, w, o);

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            if (e is CodeAssignStatement) 
            {
                System.CodeDom.CodeAssignStatement stmt = (System.CodeDom.CodeAssignStatement)e;

                string name = GetNextVar("assign");

                w.WriteLine(@"CodeAssignStatement {0} = new CodeAssignStatement();", name);

                if (stmt.Left != null)
                {
                    w.WriteLine(@"{0}.Left = {1};", name, GenerateCodeExpression(null, stmt.Left, w, o));
                }

                if (stmt.Right != null)
                {
                    w.WriteLine(@"{0}.Right = {1};", name, GenerateCodeExpression(null, stmt.Right, w, o));
                }

                GenerateCodeDirectives(string.Format(@"{0}.StartDirectives", name), stmt.StartDirectives, w, o);
                GenerateCodeDirectives(string.Format(@"{0}.EndDirectives", name), stmt.EndDirectives, w, o);

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            if (e is CodeExpressionStatement) 
            {
                System.CodeDom.CodeExpressionStatement stmt = (System.CodeDom.CodeExpressionStatement)e;

                string name = GetNextVar("expr");

                w.WriteLine(@"CodeExpressionStatement {0} = new CodeExpressionStatement();", name);

                if (stmt.Expression != null)
                {
                    w.WriteLine(@"{0}.Expression = {1};", name, GenerateCodeExpression(null, stmt.Expression, w, o));
                }

                GenerateCodeDirectives(string.Format(@"{0}.StartDirectives", name), stmt.StartDirectives, w, o);
                GenerateCodeDirectives(string.Format(@"{0}.EndDirectives", name), stmt.EndDirectives, w, o);

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            if (e is CodeIterationStatement) 
            {
                System.CodeDom.CodeIterationStatement stmt = (System.CodeDom.CodeIterationStatement)e;

                string name = GetNextVar("for");

                w.WriteLine(@"CodeIterationStatement {0} = new CodeIterationStatement();", name);

                if (stmt.InitStatement != null)
                {
                    w.WriteLine(@"{0}.InitStatement = {1};", name, GenerateCodeStatement(null, stmt.InitStatement, w, o));
                }

                if (stmt.TestExpression != null)
                {
                    w.WriteLine(@"{0}.TestExpression = {1};", name, GenerateCodeExpression(null, stmt.TestExpression, w, o));
                }

                if (stmt.IncrementStatement != null)
                {
                    w.WriteLine(@"{0}.IncrementStatement = {1};", name, GenerateCodeStatement(null, stmt.IncrementStatement, w, o));
                }

                GenerateCodeStatements(string.Format(@"{0}.Statements", name), stmt.Statements, w, o);

                GenerateCodeDirectives(string.Format(@"{0}.StartDirectives", name), stmt.StartDirectives, w, o);
                GenerateCodeDirectives(string.Format(@"{0}.EndDirectives", name), stmt.EndDirectives, w, o);

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }
            
            if (e is CodeThrowExceptionStatement) 
            {
                System.CodeDom.CodeThrowExceptionStatement stmt = (System.CodeDom.CodeThrowExceptionStatement)e;

                string name = GetNextVar("throw");

                w.WriteLine(@"CodeThrowExceptionStatement {0} = new CodeThrowExceptionStatement();", name);

                if (stmt.ToThrow != null)
                {
                    w.WriteLine(@"{0}.ToThrow = {1};", name, GenerateCodeExpression(null, stmt.ToThrow, w, o));
                }

                GenerateCodeDirectives(string.Format(@"{0}.StartDirectives", name), stmt.StartDirectives, w, o);
                GenerateCodeDirectives(string.Format(@"{0}.EndDirectives", name), stmt.EndDirectives, w, o);

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            if (e is CodeSnippetStatement) 
            {
                System.CodeDom.CodeSnippetStatement stmt = (System.CodeDom.CodeSnippetStatement)e;

                string name = GetNextVar("snippet");

                w.WriteLine(@"CodeSnippetStatement {0} = new CodeSnippetStatement();", name);

                if (stmt.Value != null)
                {
                    w.WriteLine(@"{0}.Value = ""{1}"";", name, stmt.Value);
                }

                GenerateCodeDirectives(string.Format(@"{0}.StartDirectives", name), stmt.StartDirectives, w, o);
                GenerateCodeDirectives(string.Format(@"{0}.EndDirectives", name), stmt.EndDirectives, w, o);

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            if (e is CodeVariableDeclarationStatement) 
            {
                System.CodeDom.CodeVariableDeclarationStatement stmt = (System.CodeDom.CodeVariableDeclarationStatement)e;

                string name = GetNextVar("decl");

                w.WriteLine(@"CodeVariableDeclarationStatement {0} = new CodeVariableDeclarationStatement();", name);

                if (stmt.InitExpression != null)
                {
                    w.WriteLine(@"{0}.InitExpression = {1};", name, GenerateCodeExpression(null, stmt.InitExpression, w, o));
                }

                w.WriteLine(@"{0}.Name = ""{1}"";", name, stmt.Name);

                if (stmt.Type != null)
                {
                    w.WriteLine(@"{0}.Type = {1};", name, GenerateCodeTypeReference(null, stmt.Type, w, o));
                }

                GenerateCodeDirectives(string.Format(@"{0}.StartDirectives", name), stmt.StartDirectives, w, o);
                GenerateCodeDirectives(string.Format(@"{0}.EndDirectives", name), stmt.EndDirectives, w, o);

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            if (e is CodeAttachEventStatement) 
            {
                System.CodeDom.CodeAttachEventStatement stmt = (System.CodeDom.CodeAttachEventStatement)e;

                string name = GetNextVar("addevent");

                w.WriteLine(@"CodeAttachEventStatement {0} = new CodeAttachEventStatement();", name);

                if (stmt.Event != null)
                {
                    w.WriteLine(@"{0}.Event = {1};", name, GenerateCodeEventReferenceExpression(null, stmt.Event, w, o));
                }

                if (stmt.Listener != null)
                {
                    w.WriteLine(@"{0}.Listener = {1};", name, GenerateCodeExpression(null, stmt.Listener, w, o));
                }

                GenerateCodeDirectives(string.Format(@"{0}.StartDirectives", name), stmt.StartDirectives, w, o);
                GenerateCodeDirectives(string.Format(@"{0}.EndDirectives", name), stmt.EndDirectives, w, o);

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            if (e is CodeRemoveEventStatement) 
            {
                System.CodeDom.CodeRemoveEventStatement stmt = (System.CodeDom.CodeRemoveEventStatement)e;

                string name = GetNextVar("removeevent");

                w.WriteLine(@"CodeRemoveEventStatement {0} = new CodeRemoveEventStatement();", name);

                if (stmt.Event != null)
                {
                    w.WriteLine(@"{0}.Event = {1};", name, GenerateCodeEventReferenceExpression(null, stmt.Event, w, o));
                }

                if (stmt.Listener != null)
                {
                    w.WriteLine(@"{0}.Listener = {1};", name, GenerateCodeExpression(null, stmt.Listener, w, o));
                }

                GenerateCodeDirectives(string.Format(@"{0}.StartDirectives", name), stmt.StartDirectives, w, o);
                GenerateCodeDirectives(string.Format(@"{0}.EndDirectives", name), stmt.EndDirectives, w, o);

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            if (e is CodeGotoStatement) 
            {
                System.CodeDom.CodeGotoStatement stmt = (System.CodeDom.CodeGotoStatement)e;

                string name = GetNextVar("goto");

                w.WriteLine(@"CodeGotoStatement {0} = new CodeGotoStatement();", name);

                if (stmt.Label != null)
                {
                    w.WriteLine(@"{0}.Label = ""{1}"";", name, stmt.Label);
                }

                GenerateCodeDirectives(string.Format(@"{0}.StartDirectives", name), stmt.StartDirectives, w, o);
                GenerateCodeDirectives(string.Format(@"{0}.EndDirectives", name), stmt.EndDirectives, w, o);

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }
            
            if (e is CodeLabeledStatement) 
            {
                System.CodeDom.CodeLabeledStatement stmt = (System.CodeDom.CodeLabeledStatement)e;

                string name = GetNextVar("label");

                w.WriteLine(@"CodeLabeledStatement {0} = new CodeLabeledStatement();", name);

                if (stmt.Label != null)
                {
                    w.WriteLine(@"{0}.Label = ""{1}"";", name, stmt.Label);
                }

                if (stmt.Statement != null)
                {
                    w.WriteLine(@"{0}.Statement = {1};", name, GenerateCodeStatement(null, stmt.Statement, w, o));
                }

                GenerateCodeDirectives(string.Format(@"{0}.StartDirectives", name), stmt.StartDirectives, w, o);
                GenerateCodeDirectives(string.Format(@"{0}.EndDirectives", name), stmt.EndDirectives, w, o);

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }
 
            Debug.Fail(string.Format("Generator for CodeStatement {0} Not Implemented", e.GetType().Name));

            return null;
        }

        void GenerateCodeStatements(string context, CodeStatementCollection stmts, System.IO.TextWriter w, System.CodeDom.Compiler.CodeGeneratorOptions o)
        {
            if (stmts.Count > 0)
            {
                foreach (CodeStatement stmt in stmts)
                {
                    GenerateCodeStatement(context, stmt, w, o);
                }
            }
        }

        string GenerateCodeTypeDeclaration(string context, CodeTypeDeclaration decl, System.IO.TextWriter w, System.CodeDom.Compiler.CodeGeneratorOptions o)
        {
            if (o.BlankLinesBetweenMembers)
            {
                w.WriteLine();
            }

            w.WriteLine(@"//");

            string decltype = string.Empty;

            if (decl.IsClass)
            {
                decltype = "class";
            }
            else if (decl.IsEnum)
            {
                decltype = "enum";
            }
            else if (decl.IsInterface)
            {
                decltype = "interface";
            }
            else if (decl.IsStruct)
            {
                decltype = "struct";
            }

            w.WriteLine(@"//");
            w.WriteLine(@"// {0} {1}", decltype, decl.Name);
            w.WriteLine(@"//");

            string name = GetNextVar(decl.Name + "_" + decltype);

            w.WriteLine(@"CodeTypeDeclaration {0} = new CodeTypeDeclaration(""{1}"");", name, decl.Name);
            
            w.WriteLine(@"{0}.Attributes = {1};", name, GetMemberAttributes(decl.Attributes));

            if (decl.IsClass)
            {
                w.WriteLine(@"{0}.IsClass = true;", name);
            }
            else if (decl.IsEnum)
            {
                w.WriteLine(@"{0}.IsEnum = true;", name);
            }
            else if (decl.IsInterface)
            {
                w.WriteLine(@"{0}.IsInterface = true;", name);
            }
            else if (decl.IsStruct)
            {
                w.WriteLine(@"{0}.IsStruct = true;", name);
            }
            
            GenerateCodeAttributeDeclarations(string.Format(@"{0}.CustomAttributes", name), decl.CustomAttributes, w, o); 
            
            GenerateCodeTypeReferences(string.Format(@"{0}.BaseTypes", name), decl.BaseTypes, w, o); 
            
            GenerateCodeComments(string.Format(@"{0}.Comments", name), decl.Comments, w, o);

            GenerateCodeTypeMembers(string.Format(@"{0}.Members", name), decl.Members, w, o);

            if (context != null && context.Length > 0)
            {
                w.WriteLine(@"{0}.Add({1});", context, name);
                w.WriteLine();
            }

            return name;
        }

        void GenerateCodeTypeDeclarations(string context, CodeTypeDeclarationCollection types, System.IO.TextWriter w, System.CodeDom.Compiler.CodeGeneratorOptions o)
        {
            if (types.Count > 0)
            {
                foreach (CodeTypeDeclaration decl in types)
                {
                    GenerateCodeTypeDeclaration(context, decl, w, o);
                }
            }
        }

        string GenerateCodeTypeMember(string context, CodeTypeMember m, System.IO.TextWriter w, System.CodeDom.Compiler.CodeGeneratorOptions o)
        {
            if (o.BlankLinesBetweenMembers)
            {
                w.WriteLine();
            }


            if (m is CodeConstructor)
            {
                CodeConstructor member = (CodeConstructor)m;

                string name = GetNextVar(member.Name + "_ctor");

                // parameter list
                string parameters = string.Empty;
                foreach (CodeParameterDeclarationExpression param in member.Parameters)
                {
                    if (parameters.Length > 0)
                    {
                        parameters += ", ";
                    }
                    parameters += param.Type.BaseType + " " + param.Name;
                }

                w.WriteLine(@"//");
                w.WriteLine(@"// Constructor({0})", parameters);
                w.WriteLine(@"//");

                w.WriteLine(@"CodeConstructor {0} = new CodeConstructor();", name);

                w.WriteLine(@"{0}.Attributes = {1};", name, this.GetMemberAttributes(member.Attributes));

                GenerateCodeExpressions(string.Format(@"{0}.BaseConstructorArgs", name), member.BaseConstructorArgs, w, o);

                GenerateCodeExpressions(string.Format(@"{0}.ChainedConstructorArgs", name), member.ChainedConstructorArgs, w, o);

                GenerateCodeComments(string.Format(@"{0}.Comments", name), member.Comments, w, o);

                GenerateCodeAttributeDeclarations(string.Format(@"{0}.CustomAttributes", name), member.CustomAttributes, w, o);

                GenerateCodeTypeReferences(string.Format(@"{0}.ImplementationTypes", name), member.ImplementationTypes, w, o);

                // w.WriteLine(@"{0}.Name = ""{1}"";", name, member.Name);

                GenerateCodeParameterDeclarationExpressions(string.Format(@"{0}.Parameters", name), member.Parameters, w, o);

                if (member.PrivateImplementationType != null)
                {
                    w.WriteLine(@"{0}.PrivateImplementationType = {1};", name, GenerateCodeTypeReference(null, member.PrivateImplementationType, w, o));
                }

                if (member.ReturnType != null && member.ReturnType.BaseType != "System.Void")
                {
                    string retname = GenerateCodeTypeReference(null, member.ReturnType, w, o);
                    w.WriteLine(@"{0}.ReturnType = {1};", name, retname);
                }

                GenerateCodeAttributeDeclarations(string.Format(@"{0}.ReturnTypeCustomAttributes", name), member.ReturnTypeCustomAttributes, w, o);

                GenerateCodeStatements(string.Format(@"{0}.Statements", name), member.Statements, w, o);

                GenerateCodeDirectives(string.Format(@"{0}.StartDirectives", name), member.StartDirectives, w, o);
                GenerateCodeDirectives(string.Format(@"{0}.EndDirectives", name), member.EndDirectives, w, o);

                GenerateCodeTypeParameters(string.Format(@"{0}.TypeParameters", name), member.TypeParameters, w, o);

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            if (m is CodeMemberField)
            {
                CodeMemberField member = (CodeMemberField)m;

                string name = GetNextVar(member.Name + "_field");

                w.WriteLine(@"//");
                w.WriteLine(@"// Field {0}", member.Name);
                w.WriteLine(@"//");

                w.WriteLine(@"CodeMemberField {0} = new CodeMemberField();", name);

                w.WriteLine(@"{0}.Attributes = {1};", name, this.GetMemberAttributes(member.Attributes));

                GenerateCodeComments(string.Format(@"{0}.Comments", name), member.Comments, w, o);

                GenerateCodeAttributeDeclarations(string.Format(@"{0}.CustomAttributes", name), member.CustomAttributes, w, o);

                if (member.InitExpression != null)
                {
                    string init = this.GenerateCodeExpression(null, member.InitExpression, w, o);
                    w.WriteLine(@"{0}.InitExpression = {1};", name, init);
                }

                w.WriteLine(@"{0}.Name = ""{1}"";", name, member.Name);

                if (member.Type != null)
                {
                    string type = this.GenerateCodeTypeReference(null, member.Type, w, o);
                    w.WriteLine(@"{0}.Type = {1};", name, type);
                }

                GenerateCodeDirectives(string.Format(@"{0}.StartDirectives", name), member.StartDirectives, w, o);
                GenerateCodeDirectives(string.Format(@"{0}.EndDirectives", name), member.EndDirectives, w, o);

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            if (m is CodeMemberEvent)
            {
                CodeMemberEvent member = (CodeMemberEvent)m;

                string name = GetNextVar(member.Name + "_event");

                w.WriteLine(@"//");
                w.WriteLine(@"// Event {0}", member.Name);
                w.WriteLine(@"//");

                w.WriteLine(@"CodeMemberEvent {0} = new CodeMemberEvent();", name);

                w.WriteLine(@"{0}.Attributes = {1};", name, this.GetMemberAttributes(member.Attributes));

                GenerateCodeComments(string.Format(@"{0}.Comments", name), member.Comments, w, o);
              
                GenerateCodeAttributeDeclarations(string.Format(@"{0}.CustomAttributes", name), member.CustomAttributes, w, o);

                w.WriteLine(@"{0}.Name = ""{1}"";", name, member.Name);

                if (member.Type != null)
                {
                    w.WriteLine(@"{0}.Type = {1};", name, GenerateCodeTypeReference(null, member.Type, w, o));
                }

                GenerateCodeTypeReferences(string.Format(@"{0}.ImplementationTypes", name), member.ImplementationTypes, w, o);

                if (member.PrivateImplementationType != null)
                {
                    w.WriteLine(@"{0}.PrivateImplementationType = {1};", name, GenerateCodeTypeReference(null, member.PrivateImplementationType, w, o));
                }

                GenerateCodeDirectives(string.Format(@"{0}.StartDirectives", name), member.StartDirectives, w, o);
                GenerateCodeDirectives(string.Format(@"{0}.EndDirectives", name), member.EndDirectives, w, o);

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            if (m is CodeMemberProperty)
            {
                CodeMemberProperty member = (CodeMemberProperty)m;

                string name = GetNextVar(member.Name + "_property");

                // parameter list
                string parameters = string.Empty;
                foreach (CodeParameterDeclarationExpression param in member.Parameters)
                {
                    if (parameters.Length > 0)
                    {
                        parameters += ", ";
                    }
                    parameters += param.Type.BaseType + " " + param.Name;
                }

                w.WriteLine(@"//");

                if (parameters.Length == 0)
                    w.WriteLine(@"// Property {0}", member.Name);
                else
                    w.WriteLine(@"// Property {0}[{1}]", member.Name, parameters);

                w.WriteLine(@"//");

                w.WriteLine(@"CodeMemberProperty {0} = new CodeMemberProperty();", name);

                w.WriteLine(@"{0}.Attributes = {1};", name, this.GetMemberAttributes(member.Attributes));

                GenerateCodeComments(string.Format(@"{0}.Comments", name), member.Comments, w, o);

                GenerateCodeAttributeDeclarations(string.Format(@"{0}.CustomAttributes", name), member.CustomAttributes, w, o);

                w.WriteLine(@"{0}.Name = ""{1}"";", name, member.Name);

                w.WriteLine(@"{0}.Type = {1};", name, this.GenerateCodeTypeReference(null, member.Type, w, o));

                GenerateCodeParameterDeclarationExpressions(string.Format(@"{0}.Parameters", name), member.Parameters, w, o);

                w.WriteLine(@"{0}.HasGet = {1};", name, member.HasGet.ToString().ToLower());

                if (member.HasGet)
                {
                    GenerateCodeStatements(string.Format(@"{0}.GetStatements", name), member.GetStatements, w, o);
                }

                w.WriteLine(@"{0}.HasSet = {1};", name, member.HasSet.ToString().ToLower());
                
                if (member.HasSet)
                {
                    GenerateCodeStatements(string.Format(@"{0}.SetStatements", name), member.SetStatements, w, o);
                }

                GenerateCodeTypeReferences(string.Format(@"{0}.ImplementationTypes", name), member.ImplementationTypes, w, o);

                if (member.PrivateImplementationType != null)
                {
                    w.WriteLine(@"{0}.PrivateImplementationType = {1};", name, GenerateCodeTypeReference(null, member.PrivateImplementationType, w, o));
                }

                GenerateCodeDirectives(string.Format(@"{0}.StartDirectives", name), member.StartDirectives, w, o);
                GenerateCodeDirectives(string.Format(@"{0}.EndDirectives", name), member.EndDirectives, w, o);

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;

            }

            if (m is CodeMemberMethod)
            {
                CodeMemberMethod member = (CodeMemberMethod)m;

                string name = GetNextVar(member.Name + "_method");

                // parameter list
                string parameters = string.Empty;
                foreach (CodeParameterDeclarationExpression param in member.Parameters)
                {
                    if (parameters.Length > 0)
                    {
                        parameters += ", ";
                    }
                    parameters += param.Type.BaseType + " " + param.Name;
                }

                w.WriteLine(@"//");
                w.WriteLine(@"// Function {0}({1})", member.Name, parameters);
                w.WriteLine(@"//");

                w.WriteLine(@"CodeMemberMethod {0} = new CodeMemberMethod();", name);

                w.WriteLine(@"{0}.Attributes = {1};", name, this.GetMemberAttributes(member.Attributes));

                GenerateCodeComments(string.Format(@"{0}.Comments", name), member.Comments, w, o);

                GenerateCodeAttributeDeclarations(string.Format(@"{0}.CustomAttributes", name), member.CustomAttributes, w, o);

                w.WriteLine(@"{0}.Name = ""{1}"";", name, member.Name);

                GenerateCodeParameterDeclarationExpressions(string.Format(@"{0}.Parameters", name), member.Parameters, w, o);

                if (member.ReturnType != null && member.ReturnType.BaseType != "System.Void")
                {
                    string retname = GenerateCodeTypeReference(null, member.ReturnType, w, o);
                    w.WriteLine(@"{0}.ReturnType = {1};", name, retname);
                }

                GenerateCodeTypeParameters(string.Format(@"{0}.TypeParameters", name), member.TypeParameters, w, o);

                GenerateCodeAttributeDeclarations(string.Format(@"{0}.ReturnTypeCustomAttributes", name), member.ReturnTypeCustomAttributes, w, o);

                GenerateCodeStatements(string.Format(@"{0}.Statements", name), member.Statements, w, o);

                GenerateCodeTypeReferences(string.Format(@"{0}.ImplementationTypes", name), member.ImplementationTypes, w, o);

                if (member.PrivateImplementationType != null)
                {
                    w.WriteLine(@"{0}.PrivateImplementationType = {1};", name, GenerateCodeTypeReference(null, member.PrivateImplementationType, w, o));
                }

                GenerateCodeDirectives(string.Format(@"{0}.StartDirectives", name), member.StartDirectives, w, o);
                GenerateCodeDirectives(string.Format(@"{0}.EndDirectives", name), member.EndDirectives, w, o);

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            if (m is CodeTypeDelegate)
            {
                CodeTypeDelegate member = (CodeTypeDelegate)m;

                string name = GetNextVar(member.Name + "_delegate");

                // parameter list
                string parameters = string.Empty;
                foreach (CodeParameterDeclarationExpression param in member.Parameters)
                {
                    if (parameters.Length > 0)
                    {
                        parameters += ", ";
                    }
                    parameters += param.Type.BaseType + " " + param.Name;
                }

                w.WriteLine(@"//");
                w.WriteLine(@"// Delegate {0}({1})", member.Name, parameters);
                w.WriteLine(@"//");
                
                w.WriteLine(@"CodeTypeDelegate {0} = new CodeTypeDelegate();", name);

                w.WriteLine(@"{0}.Attributes = {1};", name, this.GetMemberAttributes(member.Attributes));

                GenerateCodeTypeReferences(string.Format(@"{0}.BaseTypes", name), member.BaseTypes, w, o); 

                GenerateCodeComments(string.Format(@"{0}.Comments", name), member.Comments, w, o);

                GenerateCodeAttributeDeclarations(string.Format(@"{0}.CustomAttributes", name), member.CustomAttributes, w, o);

                w.WriteLine(@"{0}.Name = ""{1}"";", name, member.Name);

                if (member.IsClass)
                {
                    w.WriteLine(@"{0}.IsClass = true;", name);
                }

                if (member.IsEnum)
                {
                    w.WriteLine(@"{0}.IsEnum = true;", name);
                }

                if (member.IsInterface)
                {
                    w.WriteLine(@"{0}.IsInterface = true;", name);
                }

                if (member.IsPartial)
                {
                    w.WriteLine(@"{0}.IsPartial = true;", name);
                }

                if (member.IsStruct)
                {
                    w.WriteLine(@"{0}.IsStruct = true;", name);
                }

                GenerateCodeTypeMembers(string.Format(@"{0}.Members", name), member.Members, w, o);

                GenerateCodeParameterDeclarationExpressions(string.Format(@"{0}.Parameters", name), member.Parameters, w, o);

                if (member.ReturnType != null && member.ReturnType.BaseType != "System.Void")
                {
                    string retname = GenerateCodeTypeReference(null, member.ReturnType, w, o);
                    w.WriteLine(@"{0}.ReturnType = {1};", name, retname);
                }

                w.WriteLine(@"{0}.TypeAttributes = {1};", name, GetTypeAttributes(member.TypeAttributes));

                GenerateCodeTypeParameters(string.Format(@"{0}.TypeParameters", name), member.TypeParameters, w, o);

                GenerateCodeDirectives(string.Format(@"{0}.StartDirectives", name), member.StartDirectives, w, o);
                GenerateCodeDirectives(string.Format(@"{0}.EndDirectives", name), member.EndDirectives, w, o);

                if (context != null && context.Length > 0)
                {
                    w.WriteLine(@"{0}.Add({1});", context, name);
                    w.WriteLine();
                }

                return name;
            }

            Debug.Fail(string.Format("Generator for CodeTypeMember {0} Not Implemented", m.GetType().Name));

            return null;
        }

        void GenerateCodeTypeMembers(string context, CodeTypeMemberCollection members, System.IO.TextWriter w, System.CodeDom.Compiler.CodeGeneratorOptions o)
        {
            if (members.Count > 0)
            {
                foreach (CodeTypeMember member in members)
                {
                    GenerateCodeTypeMember(context, member, w, o);
                }
            }
        }

        string GenerateCodeTypeParameter(string context, CodeTypeParameter param, System.IO.TextWriter w, System.CodeDom.Compiler.CodeGeneratorOptions o)
        {
            string name = GetNextVar(param.Name + "_param");

            w.WriteLine(@"CodeTypeParameter {0} = new CodeTypeParameter();", name);

            GenerateCodeTypeReferences(string.Format(@"{0}.Constraints", context), param.Constraints, w, o); 

            GenerateCodeAttributeDeclarations(string.Format(@"{0}.CustomAttributes", name), param.CustomAttributes, w, o);

            w.WriteLine(@"{0}.HasConstructorConstraint = {1};", name, param.HasConstructorConstraint.ToString().ToLower());

            w.WriteLine(@"{0}.Name = ""{1}"";", name, param.Name);

            if (context != null && context.Length > 0)
            {
                w.WriteLine(@"{0}.Add({1});", context, name);
                w.WriteLine();
            }

            return name;
        }

        void GenerateCodeTypeParameters(string context, CodeTypeParameterCollection @params, System.IO.TextWriter w, System.CodeDom.Compiler.CodeGeneratorOptions o)
        {
            if (@params.Count > 0)
            {
                foreach (CodeTypeParameter param in @params)
                {
                    GenerateCodeTypeParameter(context, param, w, o);
                }
            }
        }

        string GenerateCodeTypeReference(string context, CodeTypeReference typeref, System.IO.TextWriter w, System.CodeDom.Compiler.CodeGeneratorOptions o)
        {
            string name = GetNextVar(typeref.BaseType + "_type");
          
            CodeTypeReference elementType = typeref.ArrayElementType;
            if (elementType == null)
            {
                if (typeref.TypeArguments.Count == 0)
                {
                    w.WriteLine(@"CodeTypeReference {0} = new CodeTypeReference(""{1}"");", name, typeref.BaseType);
                }
                else
                {
                    w.WriteLine(@"CodeTypeReference {0} = new CodeTypeReference(""{1}"",CodeTypeReferenceOptions.GenericTypeParameter);",name  , typeref.BaseType.Substring (0, typeref.BaseType.IndexOf ('`')));
                    for (int i = 0; i < typeref.TypeArguments.Count; i++)
                    {
                        w.WriteLine(@"{0} .TypeArguments.Add(""{1}"");", name, typeref.TypeArguments[i].BaseType );
                    }
                 
                   
                }
            }
            else
            {
                w.WriteLine(@"CodeTypeReference {0} = new CodeTypeReference(""{1}"", {2});", name, typeref.BaseType, typeref.ArrayRank);

                GenerateCodeTypeReference(string.Format(@"{0}.ArrayElementType", context), typeref.ArrayElementType, w, o); 
            }

            if (context != null && context.Length > 0)
            {
                w.WriteLine(@"{0}.Add({1});", context, name);
                w.WriteLine();
            }

            return name;
        }

        void GenerateCodeTypeReferences(string context, CodeTypeReferenceCollection typerefs, System.IO.TextWriter w, System.CodeDom.Compiler.CodeGeneratorOptions o)
        {
            if (typerefs.Count > 0)
            {
                foreach (CodeTypeReference typeref in typerefs)
                {
                    GenerateCodeTypeReference(context, typeref, w, o);
                }
            }
        }

        string GetMemberAttributes(MemberAttributes attributes)
        {
            string val = string.Empty;
            System.CodeDom.MemberAttributes[] attrs = (System.CodeDom.MemberAttributes[])Enum.GetValues(typeof(System.CodeDom.MemberAttributes));
            foreach (System.CodeDom.MemberAttributes attr in attrs)
            {
                if ((attr & attributes) == attr)
                {
                    if (val.Length != 0)
                        val += "|";
                    val += "MemberAttributes." + attr.ToString();
                }
            }

            if (val.Length == 0)
            {
                val = "MemberAttributes.Private";
            }

            return val;
        }

        string GetTypeAttributes(System.Reflection.TypeAttributes attributes)
        {
            string val = string.Empty;
            System.Reflection.TypeAttributes[] attrs = (System.Reflection.TypeAttributes[])Enum.GetValues(typeof(System.Reflection.TypeAttributes));
            foreach (System.Reflection.TypeAttributes attr in attrs)
            {
                if ((attr & attributes) == attr)
                {
                    if (val.Length != 0)
                        val += "|";
                    val += "System.Reflection.TypeAttributes." + attr.ToString();
                }
            }

            if (val.Length == 0)
            {
                val = "System.Reflection.TypeAttributes.Private";
            }

            return val;
        }

        string GetFieldDirection(FieldDirection attributes)
        {
            string val = string.Empty;
            System.CodeDom.FieldDirection[] attrs = (System.CodeDom.FieldDirection[])Enum.GetValues(typeof(System.CodeDom.FieldDirection));
            foreach (System.CodeDom.FieldDirection attr in attrs)
            {
                if ((attr & attributes) == attr)
                {
                    if (val.Length != 0)
                        val += "|";
                    val += "FieldDirection." + attr.ToString();
                }
            }

            if (val.Length == 0)
            {
                val = "FieldDirection.In";
            }

            return val;
        }

        string GetCodeRegionMode(CodeRegionMode attributes)
        {
            string val = string.Empty;
            System.CodeDom.CodeRegionMode[] attrs = (System.CodeDom.CodeRegionMode[])Enum.GetValues(typeof(System.CodeDom.CodeRegionMode));
            foreach (System.CodeDom.CodeRegionMode attr in attrs)
            {
                if ((attr & attributes) == attr)
                {
                    if (val.Length != 0)
                        val += "|";
                    val += "CodeRegionMode." + attr.ToString();
                }
            }

            if (val.Length == 0)
            {
                return "CodeRegionMode.None";
            }

            return val;
        }

        string GetCodeBinaryOperatorType(CodeBinaryOperatorType op)
        {
            return "CodeBinaryOperatorType." + op.ToString();
        }

        string Escape(string value)
        {
            return value;
        }

        string GetByteData(byte[] data)
        {
            if (data == null)
            {
                return "null";
            }

            string expr = "new byte[] { ";
            for (int i = 0; i < data.Length; i++)
            {
                if (i > 0)
                {
                    expr += ", ";
                }

                expr += "0x" + data[i].ToString("X2") ;
            }

            expr += " }";

            return expr;
        }
    }

    /// <summary>
    /// Output Option
    /// </summary>
    public class OutputClass
    {
        string name = string.Empty;
        AbstractAstTransformer transformer = null;
        IOutputAstVisitor prettyprinter = null;
        bool cantestcodedom = false;

        System.CodeDom.Compiler.CodeDomProvider codedomprovider = null;
        int savefilterindex = 0;

        public OutputClass()
        {
        }

        public OutputClass(string name, AbstractAstTransformer visitor, IOutputAstVisitor prettyprinter, int savefilterindex)
        {
            this.name = name;
            this.transformer = visitor;
            this.prettyprinter = prettyprinter;
            this.savefilterindex = savefilterindex;
        }

        public OutputClass(string name, System.CodeDom.Compiler.CodeDomProvider codedomprovider, int savefilterindex)
        {
            this.name = name;
            this.codedomprovider = codedomprovider;
            this.savefilterindex = savefilterindex;
        }

        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        public AbstractAstTransformer CreateTransformer()
        {
            if (transformer == null)
            {
                return null;
            }

            return (AbstractAstTransformer)transformer.GetType().Assembly.CreateInstance(transformer.GetType().FullName);
        }

        public IOutputAstVisitor CreatePrettyPrinter()
        {
            if (prettyprinter == null)
            {
                return null;
            }

            return (IOutputAstVisitor)prettyprinter.GetType().Assembly.CreateInstance(prettyprinter.GetType().FullName);
        }


        public System.CodeDom.Compiler.CodeDomProvider CodeDomProvider
        {
            get { return codedomprovider; }
            set { codedomprovider = value; }
        }

        public int SaveFilterIndex
        {
            get { return savefilterindex; }
            set { savefilterindex = value; }
        }

        public bool CanTestCodeDom
        {
            get { return cantestcodedom; }
            set { cantestcodedom = value; }
        }

        public override string ToString()
        {
            return name;
        }
    }
}
